package com.imis.base.util;

import com.imis.base.constant.CommonConstant;
import com.imis.base.constant.enums.ArgumentResponseEnum;
import com.imis.module.api.model.vo.VerificationCodeVO;
import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Random;

/**
 * <p>
 * SlideKaptchaCreatorUtil<br>
 * 滑块验证码处理类
 * </p>
 *
 * @author XinLau
 * @version 1.0
 * @since 2020年04月01日 16:35
 */
@Slf4j
public class SlideKaptchaCreatorUtil {

    /**
     * 默认图片宽度
     */
    private static int DEFAULT_IMAGE_WIDTH = 280;
    /**
     * 默认图片高度
     */
    private static int DEFAULT_IMAGE_HEIGHT = 171;

    /**
     * 生成感兴趣区域坐标
     *
     * @param verificationCode  - 验证码返回值
     * @param kaptchSourceImage - 源图
     * @param templateImage     - 模板图
     * @return 裁剪坐标
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/4/1 17:13
     */
    public static VerificationCodeVO generateCutoutCoordinates(VerificationCodeVO verificationCode, BufferedImage kaptchSourceImage, BufferedImage templateImage) {
        // 原图宽度
        DEFAULT_IMAGE_WIDTH = kaptchSourceImage.getWidth();
        // 原图高度
        DEFAULT_IMAGE_HEIGHT = kaptchSourceImage.getHeight();
        // 抠图模板宽度
        int templateImageWidth = templateImage.getWidth();
        // 抠图模板高度
        int templateImageHeight = templateImage.getHeight();
        // 随机数
        Random random = new Random(System.currentTimeMillis());
        // X坐标 取范围内坐标数据，坐标抠图一定要落在原图中，否则会导致程序错误
        int x = random.nextInt(DEFAULT_IMAGE_WIDTH - templateImageWidth) % (DEFAULT_IMAGE_WIDTH - templateImageWidth - templateImageWidth + 1) + templateImageWidth;
        // Y坐标 取范围内坐标数据，坐标抠图一定要落在原图中，否则会导致程序错误
        int y = random.nextInt(DEFAULT_IMAGE_HEIGHT - templateImageWidth) % (DEFAULT_IMAGE_HEIGHT - templateImageWidth - templateImageWidth + 1) + templateImageWidth;
        if (templateImageHeight - DEFAULT_IMAGE_HEIGHT >= 0) {
            y = random.nextInt(10);
        }
        // 设置坐标
        verificationCode.setX(x);
        verificationCode.setY(y);
        return verificationCode;
    }

    /**
     * 获取感兴趣区域
     *
     * @param x                     感兴趣区域X轴
     * @param y                     感兴趣区域Y轴
     * @param templateImageWidth    模板图宽度
     * @param templateImageHeight   模板图高度
     * @param kaptchSourceImageFile 源图
     * @return BufferedImage
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/4/1 17:13
     */
    private static BufferedImage getInterestArea(int x, int y, int templateImageWidth, int templateImageHeight, File kaptchSourceImageFile) {
        try {
            // 迭代器
            Iterator<ImageReader> imageReaderIterator = ImageIO.getImageReadersByFormatName(FileUtils.extName(kaptchSourceImageFile));
            ImageReader imageReader = imageReaderIterator.next();
            // 获取图片输入流
            ImageInputStream imageInputStream = ImageIO.createImageInputStream(kaptchSourceImageFile);
            // 图片输入流顺序读写
            imageReader.setInput(imageInputStream, true);
            // 图像读取参数
            ImageReadParam imageReadParam = imageReader.getDefaultReadParam();
            // 根据坐标生成矩形
            Rectangle rectangle = new Rectangle(x, y, templateImageWidth, templateImageHeight);
            imageReadParam.setSourceRegion(rectangle);
            return imageReader.read(0, imageReadParam);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            ArgumentResponseEnum.VERIFICATION_CODE_ERR.assertFail(e);
        }
        return null;
    }

    /**
     * 图片生成图像矩阵
     *
     * @param bufferedImage 图片源
     * @return 图片矩阵
     * @author XinLau
     * @creed The only constant is change ! !
     * @since 2020/4/1 17:13
     */
    private static int[][] getMatrix(BufferedImage bufferedImage) {
        int[][] matrix = new int[bufferedImage.getWidth()][bufferedImage.getHeight()];
        for (int i = 0; i < bufferedImage.getWidth(); i++) {
            for (int j = 0; j < bufferedImage.getHeight(); j++) {
                matrix[i][j] = bufferedImage.getRGB(i, j);
            }
        }
        return matrix;
    }

    /**
     * 根据模板图抠图
     *
     * @param interestArea  感兴趣区域图
     * @param templateImage 模板图
     * @param cutoutImage   裁剪图
     * @author XinLau
     * @creed The only constant is change ! !
     * @since 2020/4/1 17:13
     */
    private static void cutoutImageByTemplateImage(BufferedImage interestArea, BufferedImage templateImage, BufferedImage cutoutImage) {
        // 获取兴趣区域图片矩阵
        // int[][] interestAreaMatrix = getMatrix(interestArea);
        // 获取模板图片矩阵
        int[][] templateImageMatrix = getMatrix(templateImage);
        // 将模板图非透明像素设置到剪切图中
        for (int i = 0; i < templateImageMatrix.length; i++) {
            for (int j = 0; j < templateImageMatrix[0].length; j++) {
                int rgb = templateImageMatrix[i][j];
                if (rgb != 16777215 && rgb < 0) {
                    cutoutImage.setRGB(i, j, interestArea.getRGB(i, j));
                }
            }
        }
    }

    /**
     * 根据模板图生成遮罩图
     *
     * @param kaptchSourceImage 源图
     * @param templateImage     模板图
     * @param x                 感兴趣区域X轴
     * @param y                 感兴趣区域Y轴
     * @return 遮罩图
     * @author XinLau
     * @creed The only constant is change ! !
     * @since 2020/4/1 17:13
     */
    private static BufferedImage generateShadeByTemplateImage(BufferedImage kaptchSourceImage, BufferedImage templateImage, int x, int y) {
        // 根据原图，创建支持alpha通道的rgb图片
        BufferedImage shadeImage = new BufferedImage(kaptchSourceImage.getWidth(), kaptchSourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
        // 原图片矩阵
        int[][] originImageMatrix = getMatrix(kaptchSourceImage);
        // 模板图片矩阵
        int[][] templateImageMatrix = getMatrix(templateImage);
        // 将原图的像素拷贝到遮罩图
        for (int i = 0; i < originImageMatrix.length; i++) {
            for (int j = 0; j < originImageMatrix[0].length; j++) {
                int rgb = kaptchSourceImage.getRGB(i, j);
                //  获取rgb色度
                int r = (0xff & rgb);
                int g = (0xff & (rgb >> 8));
                int b = (0xff & (rgb >> 16));
                //  无透明处理
                rgb = r + (g << 8) + (b << 16) + (255 << 24);
                shadeImage.setRGB(i, j, rgb);
            }
        }
        // 对遮罩图根据模板像素进行处理
        for (int i = 0; i < templateImageMatrix.length; i++) {
            for (int j = 0; j < templateImageMatrix[0].length; j++) {
                int rgb = templateImage.getRGB(i, j);

                //  对源文件备份图像(x+i,y+j)坐标点进行透明处理
                if (rgb != 16777215 && rgb < 0) {
                    int originRgb = shadeImage.getRGB(x + i, y + j);
                    int r = (0xff & originRgb);
                    int g = (0xff & (originRgb >> 8));
                    int b = (0xff & (originRgb >> 16));
                    originRgb = r + (g << 8) + (b << 16) + (140 << 24);
                    //  对遮罩透明处理
                    shadeImage.setRGB(x + i, y + j, originRgb);
                    //  设置遮罩颜色
                    //  shadeImage.setRGB(x + i, y + j, originRgb);
                }
            }
        }
        return shadeImage;
    }

    /**
     * 根据模板图裁剪图片，生成源图、遮罩图和裁剪图
     *
     * @param kaptchSourceImageFile - 源图文件
     * @param templateImageFile     - 模板图文件
     * @param verificationCodeVO    - 验证码返回值 对象
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/4/1 17:13
     */
    public static void pictureTemplateCutout(File kaptchSourceImageFile, File templateImageFile, VerificationCodeVO verificationCodeVO) {
        try {
            // 1.读取模板图
            BufferedImage templateImage = ImageIO.read(templateImageFile);
            // 2.获取模板图宽高
            int templateImageWidth = templateImage.getWidth();
            int templateImageHeight = templateImage.getHeight();
            // 3.读取原图
            BufferedImage originImage = ImageIO.read(kaptchSourceImageFile);
            // 4.切块图   根据模板图尺寸创建一张透明图片
            BufferedImage cutoutImage = new BufferedImage(templateImageWidth, templateImageHeight, templateImage.getType());
            // 5.根据坐标获取感兴趣区域
            BufferedImage interestArea = getInterestArea(verificationCodeVO.getX(), verificationCodeVO.getY(), templateImageWidth, templateImageHeight, kaptchSourceImageFile);
            // 6.根据模板图片切图
            cutoutImageByTemplateImage(interestArea, templateImage, cutoutImage);
            // 7.图片绘图
            int bold = 5;
            Graphics2D graphics = cutoutImage.createGraphics();
            graphics.setBackground(Color.white);
            // 8.设置抗锯齿属性
            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            graphics.setStroke(new BasicStroke(bold, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
            graphics.drawImage(cutoutImage, 0, 0, null);
            graphics.dispose();
            // 9.原图生成遮罩
            BufferedImage shadeImage = generateShadeByTemplateImage(originImage, templateImage, verificationCodeVO.getX(), verificationCodeVO.getY());
            // 10.输出流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

            // 11.1源图片图片类型
            String shadeImageFileType = FileUtils.extName(kaptchSourceImageFile);
            // 源图片转为二进制字符串
            ImageIO.write(originImage, shadeImageFileType, byteArrayOutputStream);
            byte[] originImageBytes = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.flush();
            byteArrayOutputStream.reset();
            // 源图片加密成Base64字符串
            String originImageString = org.springframework.util.Base64Utils.encodeToString(originImageBytes);
            verificationCodeVO.setVerificationCode(originImageString);

            // 11.2模板图片类型
            String templateImageFileType = FileUtils.extName(templateImageFile);
            // 遮罩图片转为二进制字符串
            ImageIO.write(shadeImage, templateImageFileType, byteArrayOutputStream);
            byte[] shadeImageBytes = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.flush();
            byteArrayOutputStream.reset();
            // 遮罩图片加密成Base64字符串
            String shadeImageString = org.springframework.util.Base64Utils.encodeToString(shadeImageBytes);
            verificationCodeVO.setShadeImage(shadeImageString);

            // 11.3裁剪图片转为二进制字符串
            ImageIO.write(cutoutImage, templateImageFileType, byteArrayOutputStream);
            byte[] cutoutImageBytes = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.reset();
            // 裁剪图片加密成Base64字符串
            String cutoutImageString = org.springframework.util.Base64Utils.encodeToString(cutoutImageBytes);
            verificationCodeVO.setCutoutImage(cutoutImageString);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            ArgumentResponseEnum.VERIFICATION_CODE_ERR.assertFail(e);
        }
    }

    /**
     * 切块图描边
     *
     * @param verificationCodeVO  图片容器
     * @param borderImage         描边图
     * @param borderImageFileType 描边图类型
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/4/1 17:13
     */
    public static void cutoutImageEdge(VerificationCodeVO verificationCodeVO, BufferedImage borderImage, String borderImageFileType) {
        try {
            // 裁剪图
            String cutoutImageString = verificationCodeVO.getCutoutImage();
            // 裁剪图片解密成二进制字符
            byte[] bytes = org.springframework.util.Base64Utils.decodeFromString(cutoutImageString.replace(CommonConstant.KAPTCHA_PREFIX, CommonConstant.EMPTY));
            // 转输入流
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            // 读取裁剪图片
            BufferedImage cutoutImage = ImageIO.read(byteArrayInputStream);
            // 获取模板边框矩阵， 并进行颜色处理
            int[][] borderImageMatrix = getMatrix(borderImage);
            for (int i = 0; i < borderImageMatrix.length; i++) {
                for (int j = 0; j < borderImageMatrix[0].length; j++) {
                    int rgb = borderImage.getRGB(i, j);
                    if (rgb < 0) {
                        cutoutImage.setRGB(i, j, -7237488);
                    }
                }
            }
            // 输出流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ImageIO.write(cutoutImage, borderImageFileType, byteArrayOutputStream);
            // 新模板图描边处理后转成二进制字符串
            byte[] cutoutImageBytes = byteArrayOutputStream.toByteArray();
            // 二进制字符串加密成Base64字符串
            String cutoutImageStr = org.springframework.util.Base64Utils.encodeToString(cutoutImageBytes);
            verificationCodeVO.setCutoutImage(cutoutImageStr);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            ArgumentResponseEnum.VERIFICATION_CODE_ERR.assertFail(e);
        }
    }

}
